//
//  NSArray+maxCount.m
//  FCTools
//
//  Created by WJC on 2018/10/26.
//  Copyright © 2018 wjc. All rights reserved.
//
//user can ignore below define

#define key_errorName        @"errorName"
#define key_errorReason      @"errorReason"
#define key_errorPlace       @"errorPlace"
#define key_defaultToDo      @"defaultToDo"
#define key_callStackSymbols @"callStackSymbols"
#define key_exception        @"exception"

#define DefaultReturnNil  @"出错啦 默认值是返回nil以避免崩溃"

#define Separator         @"================================================================"
#define SeparatorWithFlag @"========================出错啦=========================="

#import "NSArray+maxCount.h"
#import <objc/runtime.h>
#import "NSObject+KVCChangeType.h"
#import "WJCDefine.h"
@implementation NSArray (maxCount)
+ (void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
//        SEL subscriptsafeSel1 = @selector(mutableObjectAtIndex:);
//        SEL subscriptunsafeSel1 = @selector(objectAtIndexedSubscript:);
//        Class class = NSClassFromString(@"__NSArrayM");
//            // 方法
//        Method subscriptsafeMethod = class_getInstanceMethod(class, subscriptsafeSel1);
//        Method subscriptunsafeMethod1 = class_getInstanceMethod(class, subscriptunsafeSel1);
//        method_exchangeImplementations(subscriptunsafeMethod1, subscriptsafeMethod);
        
        [self swizzleSelector:@selector(removeObject:)withSwizzledSelector:@selector(safeRemoveObject:)];
        [objc_getClass("__NSArrayM") swizzleSelector:@selector(addObject:) withSwizzledSelector:@selector(safeAddObject:)];
        [objc_getClass("__NSArrayM") swizzleSelector:@selector(removeObjectAtIndex:) withSwizzledSelector:@selector(safeRemoveObjectAtIndex:)];
        [objc_getClass("__NSArrayM") swizzleSelector:@selector(insertObject:atIndex:) withSwizzledSelector:@selector(safeInsertObject:atIndex:)];
        [objc_getClass("__NSPlaceholderArray") swizzleSelector:@selector(initWithObjects:count:) withSwizzledSelector:@selector(safeInitWithObjects:count:)];
        [objc_getClass("__NSArrayM") swizzleSelector:@selector(objectAtIndex:) withSwizzledSelector:@selector(safeObjectAtIndex:)];
        [objc_getClass("__NSArrayM") swizzleSelector:@selector(objectAtIndexedSubscript:) withSwizzledSelector:@selector(safeObjectAtSubscriptIndex:)];
    });

}
- (instancetype)safeInitWithObjects:(const id  _Nonnull     __unsafe_unretained *)objects count:(NSUInteger)cnt
{
    BOOL hasNilObject = NO;
    for (NSUInteger i = 0; i < cnt; i++) {
        
        if (objects[i] == nil) {
            hasNilObject = YES;
            WJCLog(@"%s object at index %lu is nil, it will be     filtered", __FUNCTION__, i);
        }
        if ([objects[i] isKindOfClass:[NSArray class]]) {
//            WJCLog(@"%@", objects[i]);
        }
    }
    
        // 因为有值为nil的元素，那么我们可以过滤掉值为nil的元素
    if (hasNilObject) {
        id __unsafe_unretained newObjects[cnt];
        NSUInteger index = 0;
        for (NSUInteger i = 0; i < cnt; ++i) {
            if (objects[i] != nil) {
                newObjects[index++] = objects[i];
            }
        }
        return [self safeInitWithObjects:newObjects count:index];
    }
    return [self safeInitWithObjects:objects count:cnt];
}

- (void)safeAddObject:(id)obj {
    if (obj == nil) {
        WJCLog(@"%s can add nil object into NSMutableArray", __FUNCTION__);
    } else {
        [self safeAddObject:obj];
    }
}
- (void)safeRemoveObject:(id)obj {
    if (obj == nil) {
        WJCLog(@"%s call -removeObject:, but argument obj is nil", __FUNCTION__);
        return;
    }
    [self safeRemoveObject:obj];
}

- (void)safeInsertObject:(id)anObject atIndex:(NSUInteger)index {
    if (anObject == nil) {
        WJCLog(@"%s can't insert nil into NSMutableArray", __FUNCTION__);
    } else if (index > self.count) {
        WJCLog(@"%s index is invalid", __FUNCTION__);
    } else {
        [self safeInsertObject:anObject atIndex:index];
    }
}

- (id)safeObjectAtIndex:(NSUInteger)index {
         id object = nil;
         
         @try {
             object = [self safeObjectAtIndex:index];
         }
         @catch (NSException *exception) {
             NSString *defaultToDo = DefaultReturnNil;
#ifdef DEBUG // 开发阶段-DEBUG阶段:使用Log
             [self noteErrorWithException:exception defaultToDo:defaultToDo];
#endif
            

         }
         @finally {
             return object;
         }
}
- (id)safeObjectAtSubscriptIndex:(NSUInteger)index {
         id object = nil;
         
         @try {
             if (self.count>index) {
                 object = [self safeObjectAtSubscriptIndex:index];
             }
             
         }
         @catch (NSException *exception) {
             NSString *defaultToDo = DefaultReturnNil;
#ifdef DEBUG // 开发阶段-DEBUG阶段:使用Log
             [self noteErrorWithException:exception defaultToDo:defaultToDo];
#endif
            

         }
         @finally {
             return object;
         }
}
- (void)safeRemoveObjectAtIndex:(NSUInteger)index {
     @autoreleasepool{
    if (self.count <= 0) {
        WJCLog(@"%s can't get any object from an empty array", __FUNCTION__);
        return;
    }
    if (index >= self.count) {
        WJCLog(@"%s index out of bound", __FUNCTION__);
        return;
    }
         [self safeRemoveObjectAtIndex:index];
   
     }
}


- (void)noteErrorWithException:(NSException *)exception defaultToDo:(NSString *)defaultToDo {
    
    //堆栈数据
    NSArray *callStackSymbolsArr = [NSThread callStackSymbols];
    
    NSString *mainCallStackSymbolMsg = [self getMainCallStackSymbolMessageWithCallStackSymbols:callStackSymbolsArr];
    
    if (mainCallStackSymbolMsg == nil) {
        
        mainCallStackSymbolMsg = @"崩溃方法定位失败,请您查看函数调用栈来排查错误原因";
        
    }
    
    NSString *errorName = exception.name;
    NSString *errorReason = exception.reason;
    errorReason = [errorReason stringByReplacingOccurrencesOfString:@"avoidCrash" withString:@""];
    
    NSString *errorPlace = [NSString stringWithFormat:@"Error Place:%@",mainCallStackSymbolMsg];
    
    NSString *logErrorMessage = [NSString stringWithFormat:@"\n\n%@\n\n%@\n%@\n%@\n",SeparatorWithFlag, errorName, errorReason, errorPlace];
    
    logErrorMessage = [NSString stringWithFormat:@"%@\n\n%@\n\n",logErrorMessage,Separator];
    WJCLog(@"%@",logErrorMessage);
    
    
    //请忽略下面的赋值，目的只是为了能顺利上传到cocoapods
    logErrorMessage = logErrorMessage;
    
    NSDictionary *errorInfoDic = @{
                                   key_errorName        : errorName,
                                   key_errorReason      : errorReason,
                                   key_errorPlace       : errorPlace,
                                   key_defaultToDo      : defaultToDo,
                                   key_exception        : exception,
                                   key_callStackSymbols : callStackSymbolsArr
                                   };
    [saveSandBox saveDataBaseClass:[NSDictionary class] andObject:errorInfoDic orType:@"error" andOther:@"error"];

}

/**
 *  获取堆栈主要崩溃精简化的信息<根据正则表达式匹配出来>
 *
 *  @param callStackSymbols 堆栈主要崩溃信息
 *
 *  @return 堆栈主要崩溃精简化的信息
 */

- (NSString *)getMainCallStackSymbolMessageWithCallStackSymbols:(NSArray<NSString *> *)callStackSymbols {
    
    //mainCallStackSymbolMsg的格式为   +[类名 方法名]  或者 -[类名 方法名]
    __block NSString *mainCallStackSymbolMsg = nil;
    
    //匹配出来的格式为 +[类名 方法名]  或者 -[类名 方法名]
    NSString *regularExpStr = @"[-\\+]\\[.+\\]";
    
    
    NSRegularExpression *regularExp = [[NSRegularExpression alloc] initWithPattern:regularExpStr options:NSRegularExpressionCaseInsensitive error:nil];
    
    
    for (int index = 2; index < callStackSymbols.count; index++) {
        NSString *callStackSymbol = callStackSymbols[index];
        
        [regularExp enumerateMatchesInString:callStackSymbol options:NSMatchingReportProgress range:NSMakeRange(0, callStackSymbol.length) usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) {
            if (result) {
                NSString* tempCallStackSymbolMsg = [callStackSymbol substringWithRange:result.range];
                
                //get className
                NSString *className = [tempCallStackSymbolMsg componentsSeparatedByString:@" "].firstObject;
                className = [className componentsSeparatedByString:@"["].lastObject;
                
                NSBundle *bundle = [NSBundle bundleForClass:NSClassFromString(className)];
                
                //filter category and system class
                if (![className hasSuffix:@")"] && bundle == [NSBundle mainBundle]) {
                    mainCallStackSymbolMsg = tempCallStackSymbolMsg;
                    
                }
                *stop = YES;
            }
        }];
        
        if (mainCallStackSymbolMsg.length) {
            break;
        }
    }
    
    return mainCallStackSymbolMsg;
}


@end
